﻿using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ThingsGateway.Foundation
{
    /// <summary>
    /// 默认实现 <see cref="IContainer" />.
    /// <br>集合内已实现了服务注册</br>
    /// </summary>
    public class Container : IContainer
    {
        #region Protected Fields

        protected readonly ConcurrentDictionary<string, ServiceDescriptor> _descriptors = new ConcurrentDictionary<string, ServiceDescriptor>();

        #endregion Protected Fields

        #region Public Methods

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        public IEnumerator<ServiceDescriptor> GetEnumerator()
        {
            return _descriptors.Values.ToList().GetEnumerator();
        }
        public bool IsRegistered(Type fromType, string key = "")
        {
            return _descriptors.ContainsKey($"{fromType.FullName}{key}");
        }

        public void Register(ServiceDescriptor descriptor, string key = "")
        {
            string k = $"{descriptor.ServiceType.FullName}{key}";
            _descriptors.AddOrUpdate(k, descriptor, (k, v) => { return descriptor; });
        }


        public object Resolve(Type fromType, object[] ps = null, string key = "")
        {
            //创建的如果是容器本身的话，返回的是区域容器
            if (fromType == typeof(IContainerProvider))
            {
                return GetScopedContainer();
            }
            string k;
            ServiceDescriptor descriptor;
            if (fromType.IsGenericType)
            {
                Type type = fromType.GetGenericTypeDefinition();
                k = $"{type.FullName}{key}";
                if (_descriptors.TryGetValue(k, out descriptor))
                {
                    if (descriptor.ImplementationFactory != null)
                    {
                        return descriptor.ImplementationFactory.Invoke(this);
                    }
                    //全局单例直接返回
                    if (descriptor.Lifetime == Lifetime.Singleton)
                    {
                        if (descriptor.ImplementationInstance != null)
                        {
                            return descriptor.ImplementationInstance;
                        }
                        lock (descriptor)
                        {
                            if (descriptor.ImplementationInstance != null)
                            {
                                return descriptor.ImplementationInstance;
                            }
                            if (descriptor.ImplementationType.IsGenericType)
                            {
                                return descriptor.ImplementationInstance = Create(descriptor.ImplementationType.MakeGenericType(fromType.GetGenericArguments()), ps);
                            }
                            else
                            {
                                return descriptor.ImplementationInstance = Create(descriptor.ImplementationType, ps);
                            }
                        }
                    }
                    if (descriptor.ImplementationType.IsGenericType)
                    {
                        return Create(descriptor.ImplementationType.MakeGenericType(fromType.GetGenericArguments()), ps);
                    }
                    else
                    {
                        return Create(descriptor.ImplementationType, ps);
                    }
                }
            }
            k = $"{fromType.FullName}{key}";
            if (_descriptors.TryGetValue(k, out descriptor))
            {
                if (descriptor.ImplementationFactory != null)
                {
                    return descriptor.ImplementationFactory.Invoke(this);
                }
                if (descriptor.Lifetime == Lifetime.Singleton)
                {
                    if (descriptor.ImplementationInstance != null)
                    {
                        return descriptor.ImplementationInstance;
                    }
                    lock (descriptor)
                    {
                        if (descriptor.ImplementationInstance != null)
                        {
                            return descriptor.ImplementationInstance;
                        }
                        return descriptor.ImplementationInstance = Create(descriptor.ImplementationType, ps);
                    }
                }
                return Create(descriptor.ImplementationType, ps);
            }
            else if (fromType.IsPrimitive || fromType == typeof(string))
            {
                return default;
            }
            else if (fromType.IsClass && !fromType.IsAbstract)
            {
                if (fromType.GetCustomAttribute<DependencyParamterInjectAttribute>() is DependencyParamterInjectAttribute attribute)
                {
                    if (attribute.ResolveNullIfNoRegistered && !IsRegistered(fromType, key))
                    {
                        return default;
                    }
                }
                return Create(fromType, ps);
            }
            else
            {
                return default;
            }
        }

        public void Unregister(ServiceDescriptor descriptor, string key = "")
        {
            string k = $"{descriptor.ServiceType.FullName}{key}";
            _descriptors.TryRemove(k, out _);
        }

        #endregion Public Methods

        #region Private Methods

        /// <summary>
        /// 创建已知类型示例，通过特性控制
        /// </summary>
        /// <param name="toType"></param>
        /// <param name="ops">构造</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private object Create(Type toType, object[] ops)
        {
            //寻找DependencyInjectAttribute特性的构造函数
            ConstructorInfo ctor = toType.GetConstructors().FirstOrDefault(x => x.IsDefined(typeof(DependencyInjectAttribute), true));
            if (ctor is null)
            {
                //如果没有被特性标记，那就取构造函数参数最多的作为注入目标
                if (toType.GetConstructors().Length == 0)
                {
                    throw new Exception($"没有找到类型{toType.FullName}的公共构造函数。");
                }
                ctor = toType.GetConstructors().OrderByDescending(x => x.GetParameters().Length).First();
            }
            else
            {
                ops ??= ctor.GetCustomAttribute<DependencyInjectAttribute>().Ps;
            }
            //寻找类的DependencyTypeAttribute特性，如果没有的话构造，属性，方法都传入依赖
            DependencyTypeAttribute dependencyTypeAttribute = null;
            if (toType.IsDefined(typeof(DependencyTypeAttribute), true))
            {
                dependencyTypeAttribute = toType.GetCustomAttribute<DependencyTypeAttribute>();
            }

            var parameters = ctor.GetParameters();
            object[] ps = new object[parameters.Length];
            //传入构造参数
            if (dependencyTypeAttribute == null || dependencyTypeAttribute.Type.HasFlag(DependencyType.Constructor))
            {
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (ops != null && ops.Length - 1 >= i)
                    {
                        ps[i] = ops[i];
                    }
                    else
                    {
                        if (parameters[i].ParameterType.IsPrimitive || parameters[i].ParameterType == typeof(string))
                        {
                            if (parameters[i].HasDefaultValue)
                            {
                                ps[i] = parameters[i].DefaultValue;
                            }
                            else
                            {
                                ps[i] = default;
                            }
                        }
                        else
                        {
                            //构造参数自动创建实例
                            if (parameters[i].IsDefined(typeof(DependencyParamterInjectAttribute), true))
                            {
                                DependencyParamterInjectAttribute attribute = parameters[i].GetCustomAttribute<DependencyParamterInjectAttribute>();
                                Type type = attribute.Type ?? parameters[i].ParameterType;

                                if (attribute.ResolveNullIfNoRegistered && !IsRegistered(type, attribute.Key))
                                {
                                    ps[i] = default;
                                }
                                else
                                {
                                    ps[i] = Resolve(type, attribute.Ps, attribute.Key);
                                }
                            }
                            else
                            {
                                ps[i] = Resolve(parameters[i].ParameterType, null);
                            }
                        }
                    }
                }
            }
            //构造实例
            object instance = Activator.CreateInstance(toType, ps);
            //传入属性依赖
            if (dependencyTypeAttribute == null || dependencyTypeAttribute.Type.HasFlag(DependencyType.Property))
            {
                var propetys = toType.GetProperties().Where(x => x.IsDefined(typeof(DependencyInjectAttribute), true));
                foreach (var item in propetys)
                {
                    if (item.CanWrite)
                    {
                        object obj;
                        if (item.IsDefined(typeof(DependencyParamterInjectAttribute), true))
                        {
                            DependencyParamterInjectAttribute attribute = item.GetCustomAttribute<DependencyParamterInjectAttribute>();
                            Type type = attribute.Type ?? item.PropertyType;
                            if (attribute.ResolveNullIfNoRegistered && !IsRegistered(type, attribute.Key))
                            {
                                obj = null;
                            }
                            else
                            {
                                obj = Resolve(type, attribute.Ps, attribute.Key);
                            }
                        }
                        else
                        {
                            obj = Resolve(item.PropertyType, null);
                        }
                        item.SetValue(instance, obj);
                    }
                }
            }
            //传入方法依赖
            if (dependencyTypeAttribute == null || dependencyTypeAttribute.Type.HasFlag(DependencyType.Method))
            {
                var methods = toType.GetMethods().Where(x => x.IsDefined(typeof(DependencyInjectAttribute), true)).ToList();
                foreach (var item in methods)
                {
                    parameters = item.GetParameters();
                    ops = item.GetCustomAttribute<DependencyInjectAttribute>().Ps;
                    ps = new object[parameters.Length];
                    for (int i = 0; i < ps.Length; i++)
                    {
                        if (ops != null && ops.Length - 1 >= i)
                        {
                            ps[i] = ops[i];
                        }
                        else
                        {
                            if (parameters[i].ParameterType.IsPrimitive || parameters[i].ParameterType == typeof(string))
                            {
                                if (parameters[i].HasDefaultValue)
                                {
                                    ps[i] = parameters[i].DefaultValue;
                                }
                                else
                                {
                                    ps[i] = default;
                                }
                            }
                            else
                            {
                                if (parameters[i].IsDefined(typeof(DependencyParamterInjectAttribute), true))
                                {
                                    DependencyParamterInjectAttribute attribute = parameters[i].GetCustomAttribute<DependencyParamterInjectAttribute>();
                                    Type type = attribute.Type ?? parameters[i].ParameterType;

                                    if (attribute.ResolveNullIfNoRegistered && !IsRegistered(type, attribute.Key))
                                    {
                                        ps[i] = default;
                                    }
                                    else
                                    {
                                        ps[i] = Resolve(type, attribute.Ps, attribute.Key);
                                    }
                                }
                                else
                                {
                                    ps[i] = Resolve(parameters[i].ParameterType, null);
                                }
                            }
                        }
                    }
                    item.Invoke(instance, ps);
                }
            }
            return instance;
        }
        /// <summary>
        /// 获取新的区域容器，方法内对字典中的每个对象都重新赋值到新的区域容器中
        /// </summary>
        /// <returns></returns>
        private IContainerProvider GetScopedContainer()
        {
            Container service = new Container();
            foreach (var item in _descriptors)
            {
                if (item.Value.Lifetime == Lifetime.Scoped)
                {
                    service._descriptors.AddOrUpdate(item.Key, new ServiceDescriptor(item.Value.ServiceType, item.Value.ImplementationType, Lifetime.Singleton), (k, v) => { return v; });
                }
                else
                {
                    service._descriptors.AddOrUpdate(item.Key, item.Value, (k, v) => { return v; });
                }
            }
            return new ScopedContainer(service);
        }
        #endregion Private Methods

    }
}
